package caches

import (
	"context"
	"errors"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"github.com/go-redis/redis/v8"
	"strings"
	"time"
)

type RedisStore struct {
	rd        *redis.Client
	expired   time.Duration
	keyPrefix string

	Codec interfaces.Codec
}

func (store *RedisStore) Get(key string) (string, bool, error) {
	key = store.key(key)
	tmp, err := store.rd.Get(context.Background(), key).Result()
	if errors.Is(err, redis.Nil) {
		return tmp, false, nil
	}
	if err != nil {
		return tmp, false, err
	}
	return tmp, true, nil
}

func (store *RedisStore) Set(key string, value string) error {
	return store.SetExpire(key, value, store.expired)
}

func (store *RedisStore) SetExpire(key string, value string, expired time.Duration) error {
	key = store.key(key)
	_, err := store.rd.Set(context.Background(), key, value, expired).Result()
	return err
}

func (store *RedisStore) TTL(key string) (time.Duration, error) {
	key = store.key(key)
	t, err := store.rd.TTL(context.Background(), key).Result()
	if errors.Is(err, redis.Nil) {
		return -1, nil
	}
	return t, nil
}

func (store *RedisStore) Inc(key string, n int64) error {
	key = store.key(key)
	return store.rd.IncrBy(context.Background(), key, n).Err()
}

func (store *RedisStore) Dec(key string, n int64) error {
	key = store.key(key)
	return store.rd.DecrBy(context.Background(), key, n).Err()
}

func (store *RedisStore) GetSet(key string, value string, expired time.Duration) (string, error) {
	return store.GetSetFunc(key, func() string {
		return value
	}, expired)
}

func (store *RedisStore) GetSetFunc(key string, f func() string, expired time.Duration) (string, error) {
	tmp, exists, err := store.Get(key)
	if err != nil {
		return "", err
	}
	if !exists {
		d := f()
		err1 := store.SetExpire(key, d, expired)
		return d, err1
	}
	return tmp, nil
}

func (store *RedisStore) Delete(key string) error {
	key = store.key(key)
	_, err := store.rd.Del(context.Background(), key).Result()
	return err
}

func (store *RedisStore) Exist(key string) (bool, error) {
	key = store.key(key)
	d, err := store.rd.Exists(context.Background(), key).Result()
	return d > 0, err
}

func (store *RedisStore) Keys() ([]string, error) {
	key := store.key("*")
	keys, err := store.rd.Keys(context.Background(), key).Result()
	if err != nil {
		return nil, err
	}

	if store.keyPrefix == "" {
		return keys, nil
	}

	var retKeys []string
	for _, k := range keys {
		oriK, _ := strings.CutPrefix(k, store.keyPrefix)
		retKeys = append(retKeys, oriK)
	}
	return retKeys, nil
}

func (store *RedisStore) key(key string) string {
	return store.keyPrefix + key
}

func (store *RedisStore) SetKeyPrefix(prefix string) {
	store.keyPrefix = prefix
}

func NewRedisStore(rd *redis.Client, expired time.Duration) *RedisStore {
	return &RedisStore{
		rd:      rd,
		expired: expired,
	}
}
